Skip to main content

Context framework

DXcharts manages state with Context, an open-source framework developed by Devexperts.

It serves two main purposes:

  • State management (similar to Redux)
  • Code organization

It defines dependencies and resolves them at compile time (not runtime) using TypeScript and the fp-ts library (functional programming methods). This approach helps catch errors early and improves type safety.

If you have experience with Angular’s injector, Context works in a similar way, but with enhanced type safety. If you are familiar with Spring (Java), it is also similar to compile-time dependency resolution.

Under the hood, Context is based on the Reader monad from fp-ts library. When evaluated, it returns a Sink containing both a value and any associated effects.


Basic example

Here’s a simple example showing how to create and compose Context instances:

import { context } from '@dx-private/dxchart5-react/dist/context/context2';
const one = context.of(1);
const two = context.of(2);
const three = context.combine(one, two, (o, t) => {
return o + t;
});
console.log(three(null).value); // prints 3

This example demonstrates creating Context instances and combining them using combine.


Practical example

The following example shows a more practical use case:

import { context } from '@dx-private/dxchart5-react/dist/context/context2';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { DateFormatter, MathPackage, Order, OrderConfirmation, OrderProvider } from './context-example';
const OrderConfirmationContainer = context.combine(
context.key<MathPackage>()('mathPackage'), // specify dependency type and name
context.key<DateFormatter>()('dateFormatter'),
context.key<OrderProvider>()('orderProvider'),
(mathPackage, dateFormatter, orderProvider) => {
const currentOrder = orderProvider.getCurrentOrder();
const stocksPriceFormatter = (price: number) => {
return mathPackage.toFixedPrecision(price, 2) + '$';
};
return (
<OrderConfirmation
order={currentOrder}
priceFormatter={stocksPriceFormatter}
dateFormatter={dateFormatter.format}
/>
);
},
);
// provide dependency instance here
const orderProvider = {
//@ts-ignore
getCurrentOrder(): Order {
// return undefined;
},
};
const mathPackage = {
sum(a: number, b: number): number {
return a + b;
},
toFixedPrecision(n: number, base: number): string {
return n.toFixed(base);
},
};
const dateFormatter = {
format(timestamp: number): string {
return new Date(timestamp).toString();
},
};
const OrderConfirmationComponent = OrderConfirmationContainer({
dateFormatter, // will not compile until all 3 dependencies are provided with correct type
orderProvider,
mathPackage,
});
const root = createRoot(document.body);
root.render(OrderConfirmationComponent.value);

In this example, OrderConfirmationContainer is created using three dependencies defined with context.key.

Until all dependencies are resolved with the correct types, you cannot create an instance of OrderConfirmationContainer. This ensures type safety during development.


Context utility functions

Context is a monad and includes several utility functions to support functional programming patterns:

  • sequenceT: Combines multiple contexts into a single context.
  • sequenceArray: Combines an array of contexts into a single context.
  • chain: Chains contexts sequentially.
  • flatten: Flattens nested contexts.
  • map: Maps a value within a context.
  • of: Wraps a value in a context.